#include <my_head.h>

// 定义无名信号量
sem_t sem;

typedef struct _Msg{
    int acceptfd;
    struct sockaddr_in clientaddr;
}msg_t;

void *task_func(void *arg){
    pthread_detach(pthread_self()); // 标记分离态
    // 先把参数接下来
    msg_t msg = *(msg_t *)arg;
    sem_post(&sem); // 释放信号量

    int nbytes = 0;
    char buff[128] = {0};
    printf("客户端[%s:%d]连接到服务器..\n", 
        inet_ntoa(msg.clientaddr.sin_addr), ntohs(msg.clientaddr.sin_port));
    while(1){
        // 收发数据
        // 注意：由已经设置过超时时间的sockfd产生的acceptfd会继承超时属性
        // 如果想使用一样的超时时间 就无需重新设置了
        // 如果不想使用一样的超时时间 可以对每一个acceptfd单独调用 setsockopt函数来设置
        // 接收客户端发来的数据
        if(-1 == (nbytes = recv(msg.acceptfd, buff, sizeof(buff), 0))){
            if(EAGAIN == errno){
                // 说明是超时导致的错误
                printf("客户端[%s:%d]已经5秒没有发来数据了..\n", \
                    inet_ntoa(msg.clientaddr.sin_addr), ntohs(msg.clientaddr.sin_port));
            }else{
                perror("recv error");
            }
            break;
        }else if(0 == nbytes){
            printf("客户端[%s:%d]断开连接..\n", \
                inet_ntoa(msg.clientaddr.sin_addr), ntohs(msg.clientaddr.sin_port));
            break;
        }
        if(!strcmp(buff, "quit")){
            printf("客户端[%s:%d]退出了..\n", 
                inet_ntoa(msg.clientaddr.sin_addr), ntohs(msg.clientaddr.sin_port));
            break;
        }
        // 处理数据（打印到终端）
        printf("客户端[%s:%d]发来数据[%s]\n", \
                inet_ntoa(msg.clientaddr.sin_addr), ntohs(msg.clientaddr.sin_port), buff);
        // 组装应答消息
        strcat(buff, "--beijing");
        // 给客户端发送应答消息
        if(-1 == send(msg.acceptfd, buff, sizeof(buff), 0)){
            perror("send error");
            break;
        }
    }
    // 关闭套接字
    close(msg.acceptfd);
    pthread_exit(NULL);
}

int main(int argc, const char *argv[]){
    if(3 != argc){
        printf("Usage : %s <IP> <PORT>\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    // 创建套接字
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(-1 == sockfd)
        ERR_LOG("socket error");
    
    // 填充服务器网络信息结构体
    struct sockaddr_in serveraddr;
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
    socklen_t serveraddr_len = sizeof(serveraddr);

    // 将套接字和服务器网络信息结构体绑定
    if(-1 == bind(sockfd, (struct sockaddr *)&serveraddr, serveraddr_len))
        ERR_LOG("bind error");

    // 将套接字设置成被动监听状态
    if(-1 == listen(sockfd, 5))
        ERR_LOG("listen error");
    
    // 定义结构体 保存客户端的信息
    struct sockaddr_in clientaddr;
    socklen_t clientaddr_len = sizeof(clientaddr);

    // 初始化无名信号量
    sem_init(&sem, 0, 1);

    msg_t msg;
    // 阻塞等待客户端连接
    int acceptfd = 0;
    int ret = 0;
    pthread_t tid = 0;

    // 设置sockfd的接收超时时间
    struct timeval tm;
    tm.tv_sec = 5;
    tm.tv_usec = 0;
    if(-1 == setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tm, sizeof(tm)))
        ERR_LOG("setsockopt error");

    while(1){
        sem_wait(&sem); // 获取信号量
        if(-1 == (acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &clientaddr_len))){
            if(EAGAIN == errno){
                // 超时导致的错误 实际开发时 超时后如何处理取决于项目要求
                printf("已经5秒没有新的客户端连接到服务器了..\n");
                sem_post(&sem);
                continue;
            }else{
                ERR_LOG("accept error");
            }
        }
        msg.acceptfd = acceptfd;
        msg.clientaddr = clientaddr;
        // 创建子线程处理读写任务
        if(0 != (ret = pthread_create(&tid, NULL, task_func, &msg))){
            printf("pthread_create error : [%d] [%s]\n", ret, strerror(ret));
            exit(EXIT_FAILURE);
        }
    }
    // 关闭套接字
    close(sockfd);
    // 销毁无名信号量
    sem_destroy(&sem);

    return 0;
}